﻿using ActionsJudge;
using CommunicationEnvironment;
using DataType;
using DataType.Actions;
using DataType.CustomEnumType;
using DataType.JudgeResults;
using DataType.MessageType;
using DataType.ShootJudgeTables;
using MapCalclator;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OprsSimulator.Operators;
using OprsSimulator.OprAttributes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Policy;
using static System.Collections.Specialized.BitVector32;

namespace GameEvolve
{
    public class GameEvolver
    {
        /// <summary>
        /// 构造函数从文件中读取相关的信息（想定、地图）
        /// 根据文件信息为属性赋值
        /// </summary>
        public GameEvolver(string scenario_path, string map_path, string shoot_judge_table_path)
        {
            //取出当前目录下的全部.json文件，只有一个json文件时读取，否则报错
            // 读取当前目录下的全部文件的路径
            string[] scenario_file = Directory.GetFiles(scenario_path, "*.json", SearchOption.TopDirectoryOnly);
            // 如果文件数量为1
            if (scenario_file.Length == 1)
            {
                // 使用 流 读取文件路径下文件的数据
                using (StreamReader r = new StreamReader(scenario_file[0]))
                {
                    // 读出数据保存成字符串
                    string origin_scenario_data = r.ReadToEnd();

                    // 初始化态势信息
                    Dictionary<string, object> scenario_data = JsonConvert.DeserializeObject<Dictionary<string, object>>(origin_scenario_data);// 将想定文件里的数据读出为字符串格式，然后反序列化为字典类型的数据scenario_data
                    ObservationMsg curObservation = new ObservationMsg();// 新建一个ObservationMsg类型的结构体字段curObservation，用于保存一个step的态势的数据
                    curObservation.operators = JsonConvert.DeserializeObject<List<OperatorMsg>>(scenario_data["operators"].ToString());// 取出想定数据里以operators为键的数据，反序列化为List<OperatorMsg>类型，存储到curObservation的operators字段中
                    curObservation.passengers = new List<OperatorMsg>();// 初始化态势curObservation中的乘客字段，赋予字段一个地址，使其不为null，在oprsSim字段初始化完成后，调用oprsSim内部的方法以on_board为标志位将operators内部的乘客分出来
                    curObservation.cities = JsonConvert.DeserializeObject<CityMsg[]>(scenario_data["cities"].ToString());// 取出想定数据里以cities为键的数据，反序列化为CityMsg[]类型，存储到curObservation的cities字段中
                    curObservation.actions = new List<ActionMsg>();// 初始化actions，最初的态势中没有动作指令
                    curObservation.valid_actions = new Dictionary<int, Dictionary<int, List<ActionMsg>>>();// 初始化valid_actions生成算子的合法地址需要调用UpdateValidActions()方法，需要observation.operators、observation.cities、map、shootJudgeTab，在后面数据完备了调用方法初始化
                    curObservation.judge_info = new List<JudgeInfoMsg>();// 初始化judge_info，最初的态势中没有裁决信息
                    curObservation.jm_points = new List<JMPointsMsg>();// 初始化jm_points，最初的态势中没有间瞄点

                    //在更新分数之前求出并保存最大分数
                    // 循环遍历curObservation.operators
                    foreach (OperatorMsg opr in curObservation.operators)
                    {
                        // 红方算子
                        if (opr.color == (int)Color.Red)
                        {
                            // 把每点生命值的分数乘上最大生命值得到每个算子的分数
                            red_remain_max += (opr.value * opr.max_blood);
                        }
                        // 蓝方算子
                        else if (opr.color == (int)Color.Blue)
                        {
                            blue_remain_max += (opr.value * opr.max_blood);
                        }
                    }
                    // 调用方法计算双方的得分
                    curObservation.scores = UpdateScores(curObservation.operators, curObservation.passengers, curObservation.cities);
                    curObservation.time = JsonConvert.DeserializeObject<TimeMsg>(scenario_data["time"].ToString());// 取出想定数据里以time为键的数据，反序列化为TimeMsg类型，存储到curObservation的time字段中
                    // 赋值给observation属性
                    observation = curObservation;
                }
            }
            else throw new Exception($"想定文件路径中没有或者有多个.json文件");

            // 用地图文件路径构造Map类对象
            Map = new Map(map_path);
            // 用文件路径构造射击裁决表的类对象和间瞄射击裁决表的类对象
            shootJudgeTab = new ShootJudgeTable(shoot_judge_table_path);
            indirectedshootJudgeTab = new IndirectedShootJudgeTable(shoot_judge_table_path);

            // 构造OprsSimulator类对象，（后面会用 OperatorMsg结构体 构造 实现IOperator接口的算子类对象，并保存到oprsSim内
            oprsSim = new OprsSimulator.OprsSimulator();
            // DeadOprs用于记录每次step更新中死亡的算子
            oprsSim.DeadOprs = new List<IOperator>();
            // 循环遍历observation.operators
            foreach (OperatorMsg operatorMsg in observation.operators)
            {
                // 如果可以根据operatorMsg的sub_type得到枚举类型的sub_type
                if (Enum.TryParse<OprSubType>(operatorMsg.sub_type.ToString(), out OprSubType sub_type))
                {
                    // 根据sub_type实例化对应的算子类
                    IOperator opr = (IOperator)Activator.CreateInstance(Type.GetType("OprsSimulator.Operators." + sub_type.ToString() + "Opr, OprsSimulator")); //"Namespace.TypeName, AssemblyName"    命名空间.类名, 程序集名字
                    // 根据operatorMsg给类对象opr内的属性赋值
                    opr.SetUp(operatorMsg);
                    // 添加到oprsSim的Operators属性中
                    oprsSim.Operators.Add(opr);
                }
                // 如果转换失败，说明是没有记录的算子类型
                else Trace.TraceError($"算子类型{operatorMsg.sub_type.ToString()}不存在！");
            }
            // 调用OprsSimulator类内部的AssignOperators方法，将OprsSimulator类的Operators属性中乘客转移到Passengers属性中
            oprsSim.AssignOperators();
            // 调用GetOprMsgs方法，将类对象转换为结构体类型的数据，保存到observation属性的operators和passengers中
            observation.operators = GetOprMsgs(oprsSim.Operators);
            observation.passengers = GetOprMsgs(oprsSim.Passengers);
            // 更新ValidActions需要的数据较多，所以最后更新
            observation.valid_actions = UpdateValidActions(observation.operators, observation.cities, map, shootJudgeTab);

            // 将oprsSim内的算子数据同步到GameEvolver类中
            oprs = oprsSim.Operators;
            passengers = oprsSim.Passengers;

            // 此处更新FullObservation，为全局态势添加战争迷雾，FullObservation[-1]是全局态势，FullObservation[0]是红方视角，FullObservation[1]是蓝方视角
            FullObservation = PartitionObservation(observation, oprsSim.DeadOprs);
            // 游戏终止标志位
            done = false;
            // 当前步长
            curStep = 0;
            // 初始化复盘数据，保存初始态势
            replayData = new List<ObservationMsg> { observation };
            //
            comm_env = new CommEnv();
        }

        // 用于批量管理算子（删减、乘客分配、更新属性）
        private OprsSimulator.OprsSimulator oprsSim;
        public OprsSimulator.OprsSimulator OprsSim { get => oprsSim; set => oprsSim = value; }

        // 保存算子
        private List<IOperator> oprs;
        public List<IOperator> Oprs { get => oprs; set => oprs = value; }

        // 保存乘客
        private List<IOperator> passengers;
        public List<IOperator> Passengers { get => passengers; set => passengers = value; }

        private Map map;// Map为工具类，构造类对象以调用类中的地图相关的计算方法
        public Map Map { get => map; set => map = value; }

        // 保存态势
        private ObservationMsg observation;
        public ObservationMsg Observation { get => observation; set => observation = value; }

        //fullObservation[-1]为全局态势，0索引为将要发送给红方的态势，1索引为将要发给蓝方的态势
        private Dictionary<int, ObservationMsg> fullObservation;
        public Dictionary<int, ObservationMsg> FullObservation { get => fullObservation; set => fullObservation = value; }

        private ShootJudgeTable shootJudgeTab;  // 射击裁决表（射击裁决使用）
        private IndirectedShootJudgeTable indirectedshootJudgeTab;// 间瞄射击裁决表

        private bool done;  // 推演结束的标志
        public bool Done { get => done; set => done = value; }

        private List<ObservationMsg> replayData;// 存储复盘数据
        public List<ObservationMsg> ReplayData { get => replayData; set => replayData = value; }

        // 保存最大分数
        private int blue_remain_max;
        private int red_remain_max;
        // 保存当前步长（UpdateTime方法里更新）
        private int curStep;
        //
        private CommEnv comm_env;
        public CommEnv CommEnv { get => comm_env; set => comm_env = value; }

        public void Step(List<ActionMsg> actMsgs)
        {
            // 去重复、多单个算子的多个动作，去validaction中没有的动作
            List<ActionMsg> newActionMsg = UpdateActions(actMsgs);
            // 将actionmsg类型转成ibasicaction
            List<IBasicAction> actions = GetActions(newActionMsg);
            
            // 1. 对actions进行裁决，形成裁决结果，分发给operators

            // 间瞄打击判断，找出会被间瞄打击的算子，与action一起裁决
            Dictionary<JMPointsMsg, List<OperatorMsg>> jmJudgeObjs = CheckJMJudge(replayData);
            // judgeResults用于保存裁决结果
            List<IJudgeResult> judgeResults = new List<IJudgeResult>();
            // 调用裁决模块的裁决方法，裁决方法会返回裁决结果（参数：动作，算子，乘客，地图，夺控点，间瞄裁决数据，直瞄裁决表，间瞄裁决表）
            judgeResults = ActionsJudge.ActionsJudge.ToJudge(actions, oprs, passengers, map, observation.cities, jmJudgeObjs, shootJudgeTab, indirectedshootJudgeTab, observation.operators, comm_env);
            
            // 2. 各算子更新独立属性

            OprsSim.UpdateStandalone(judgeResults, map);
            // 更新oprs字段
            for (int i = 0; i < oprsSim.Operators.Count; i++)
            {
                for (int j = 0; j < oprs.Count; j++)
                {
                    if (oprsSim.Operators[i].ObjID == oprs[j].ObjID)
                    {
                        oprs[j] = oprsSim.Operators[i];
                        break;
                    }
                }
            }
            // 乘客更新属性
            oprsSim.UpdatePassengers(judgeResults, oprs, map);
            // 重新分配算子
            oprsSim.AssignOperators();
            // 更新oprs字段
            for (int i = 0; i < oprsSim.Operators.Count; i++)
            {
                for (int j = 0; j < oprs.Count; j++)
                {
                    if (oprsSim.Operators[i].ObjID == oprs[j].ObjID)
                    {
                        oprs[j] = oprsSim.Operators[i];
                        break;
                    }
                }
            }
            // 更新passengers字段
            for (int i = 0; i < oprsSim.Passengers.Count; i++)
            {
                for (int j = 0; j < passengers.Count; j++)
                {
                    if (oprsSim.Passengers[i].ObjID == passengers[j].ObjID)
                    {
                        passengers[j] = oprsSim.Passengers[i];
                        break;
                    }
                }
            }

            // 3. 各算子更新交叉属性

            OprsSim.UpdateAssociated(oprs, passengers, map);
            // 更新oprs字段
            for(int i = 0; i < oprsSim.Operators.Count; i++)
            {
                for (int j = 0; j < oprs.Count; j++)
                {
                    if (oprsSim.Operators[i].ObjID == oprs[j].ObjID)
                    {
                        oprs[j] = oprsSim.Operators[i];
                        break;
                    }
                }
            }

            // 4. 更新其它态势信息

            ObservationMsg curObservation = new ObservationMsg();// 保存新的态势信息
            // 先更新time，以确保judgeinfo中的time属性正确记录
            curObservation.time = UpdateTime(observation.time, ref curStep);// ref为地址传递，值类型当引用类型用
            curObservation.operators = GetOprMsgs(oprs);// IOperator => OperatorMsg
            curObservation.passengers = GetOprMsgs(passengers);
            curObservation.cities = UpdateCities(judgeResults, observation.cities);// 更新夺控点信息
            curObservation.actions = actMsgs;// 保存动作
            curObservation.valid_actions = UpdateValidActions(curObservation.operators, curObservation.cities, map, shootJudgeTab);// 更新合法动作
            curObservation.judge_info = UpdateJudgeInfo(judgeResults, curObservation.time);// 更新裁决信息
            curObservation.jm_points = UpdateJMPoints(judgeResults, observation.jm_points, curObservation.operators);// 更新间瞄点信息
            curObservation.scores = UpdateScores(curObservation.operators, curObservation.passengers, curObservation.cities);// 更新双方得分
            observation = curObservation;// 保存更新好的态势

            FullObservation = PartitionObservation(observation, oprsSim.DeadOprs);// 将全局态势添加战争迷雾，如何分成将要发给红方的态势和将要发给蓝方的态势

            replayData.Add(observation);// 把更新好的态势存入复盘数据中
            //导出replay data.json文件
            if (done)
            {
                //D:\C#\wargame-for-distribution\WargameEngine\bin\Debug    ..\表示返回到上一级目录    ..\\..\\..\\..\\Replay data\\  或者  \\..\\..\\..\\Replay data\\  都可以
                string replayDataDir = Directory.GetCurrentDirectory() + "\\..\\..\\..\\Replay data\\";
                if (!Directory.Exists(replayDataDir))
                {
                    Directory.CreateDirectory(replayDataDir);
                }
                //Formatting.Indented参数用于在生成的JSON字符串中添加缩进和换行符
                string json = JsonConvert.SerializeObject(replayData, Formatting.Indented);
                string fileName = "replay_" + DateTime.Now.ToString("yyyyMMddHHmm") + ".json";
                File.WriteAllText(replayDataDir + fileName, json);
            }

            //Trace.TraceInformation(observation.time.cur_step.ToString() + done.ToString());
        }
        /// <summary>
        /// 检测是否有算子受到间瞄攻击，并返回算子和间瞄点
        /// </summary>
        private Dictionary<JMPointsMsg, List<OperatorMsg>> CheckJMJudge(List<ObservationMsg> replayData)
        {
            // 间瞄裁决检测
            // 以间瞄点为键，会被此间瞄点裁决的算子为值，保存为字典形式的字段
            Dictionary<JMPointsMsg, List<OperatorMsg>> jmJudgeObjs = new Dictionary<JMPointsMsg, List<OperatorMsg>>();
            // 遍历当前态势正在轰炸的间瞄点，如果没有就返回
            if (replayData[replayData.Count - 1].jm_points == null) return jmJudgeObjs;
            if (replayData[replayData.Count - 1].jm_points.Count == 0) return jmJudgeObjs;
            foreach (JMPointsMsg jmp in replayData[replayData.Count - 1].jm_points)
            {
                // 如果是正在轰炸的间瞄点
                if (jmp.status == (int)JMPointStatusType.Exploding)
                {
                    List<OperatorMsg> judgedOpr = new List<OperatorMsg>();
                    // 遍历当前态势位于间瞄点的算子
                    foreach (OperatorMsg opr in replayData[replayData.Count - 1].operators)
                    {
                        // 非地面算子不裁决
                        if (opr.type != (int)OprType.Vehicle && opr.type != (int)OprType.Infantry) continue;
                        // 如果算子位于间瞄点
                        if (opr.cur_hex == jmp.pos)
                        {
                            // 刚刚开始轰炸的间瞄点，裁决全部格子中的算子
                            if (jmp.boom_time == 0)
                            {
                                judgedOpr.Add(opr);
                            }
                            else
                            {
                                // 在轰炸中，裁决刚进入轰炸区的算子（当前态势的位置与上一个态势的位置不一样）
                                foreach (OperatorMsg op in replayData[replayData.Count - 2].operators)
                                {
                                    if (op.obj_id == opr.obj_id)
                                    {
                                        // 与上一个态势的位置不一样
                                        if (op.cur_hex != opr.cur_hex)
                                        {
                                            judgedOpr.Add(opr);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    // 保存间瞄点和会被裁决的算子
                    if (judgedOpr.Count > 0)
                    {
                        jmJudgeObjs.Add(jmp, judgedOpr);
                    }
                }
            }
            return jmJudgeObjs;
        }
        /// <summary>
        /// 根据oprs的信息，更新valid_actions
        /// </summary>
        private Dictionary<int, Dictionary<int, List<ActionMsg>>> UpdateValidActions(List<OperatorMsg> operators, CityMsg[] cities, Map map, ShootJudgeTable shootJudgeTab)
        {
            return oprsSim.ConfirmValidActions(operators, cities, map, shootJudgeTab);// 调用OprsSimulator类的方法
        }
        /// <summary>
        /// 从judgeResults中选出与间瞄计划和取消间瞄计划，来新增或者移去间瞄点，以及更新已经存在的间瞄点属性
        /// </summary>
        /// <returns></returns>
        private List<JMPointsMsg> UpdateJMPoints(List<IJudgeResult> judgeResults, List<JMPointsMsg> oldJMPoints, List<OperatorMsg> oprs)
        {
            List<JMPointsMsg> jmPoints = new List<JMPointsMsg>();// 复制上一个态势的间瞄点信息，然后更新

            if (oldJMPoints == null) oldJMPoints = new List<JMPointsMsg>();// 如果上一个态势的间瞄点信息为空就新建一个，防止空引用
            // 深度拷贝
            //string jsonStr = JsonConvert.SerializeObject(oldJMPoints);
            //jmPoints = JsonConvert.DeserializeObject<List<JMPointsMsg>>(jsonStr);
            if (oldJMPoints.Count > 0)// 有间瞄点信息
            {
                for (int i = 0; i < oldJMPoints.Count; i++)
                {
                    JMPointsMsg jmp = oldJMPoints[i];
                    jmPoints.Add(new JMPointsMsg // 复制
                    {
                        obj_id = jmp.obj_id,// 炮兵id
                        weapon_id = jmp.weapon_id,// 武器id
                        pos = jmp.pos,// 间瞄点位置
                        boom_time = jmp.boom_time,// 轰炸时间
                        status = jmp.status,// 间瞄点状态（飞行，轰炸）
                        fly_time = jmp.fly_time// 飞行时间
                    });
                }
            }
            // 移除-->更新-->新增（先移除，防止因为先更新导致间瞄点进入轰炸状态，进行无意义的校射计算；在更新后新增，以实现0-150秒的计时）
            // 移除间瞄点
            List<JMPointsMsg> newJMPoints = new List<JMPointsMsg>();// 用于保存新增间瞄点数据，最后添加
            List<JMPointsMsg> cancelJMPoints = new List<JMPointsMsg>();// 保存取消间瞄计划的间瞄点
            foreach (var judgeResult in judgeResults)
            {
                if (judgeResult.ActionType == ActionType.JMPlan && judgeResult.IsSuccess)// 如果裁决结果的动作类型是间瞄计划，且成功执行
                {
                    JMPlanJudgeResult jmPlanJudgeResult = (JMPlanJudgeResult)judgeResult;// 强转为间瞄计划裁决结果

                    JMPointsMsg jMPointsMsg = new JMPointsMsg// 新增间瞄点信息
                    {
                        obj_id = jmPlanJudgeResult.ObjID,
                        weapon_id = (int)jmPlanJudgeResult.WeaponId,
                        pos = jmPlanJudgeResult.Pos,
                        status = (int)JMPointStatusType.Flying,
                        fly_time = 0,
                        boom_time = 0
                    };

                    newJMPoints.Add(jMPointsMsg);// 添加至newJMPoints
                }
                if (judgeResult.ActionType == ActionType.CancelJMPlan && judgeResult.IsSuccess)// 裁决结果的动作类型为取消间瞄计划，且成功执行
                {
                    CancelJMPlanJudgeResult jmPlanJudgeResult = (CancelJMPlanJudgeResult)judgeResult;// 强转为取消间瞄计划裁决结果

                    foreach (var jmp in jmPoints)// 找到被取消的间瞄点
                    {
                        if (jmp.obj_id == jmPlanJudgeResult.ObjID && jmp.status == (int)JMPointStatusType.Flying)//防止一个算子有两个间瞄点，加一个正在飞行的判断（两个间瞄点可能同位置，用pos判断不合适）
                        {
                            cancelJMPoints.Add(jmp);// 添加至cancelJMPoints
                        }
                    }                 
                }
            }
            if (cancelJMPoints.Count > 0)// 如果有被取消的间瞄点
            {
                foreach (var p in cancelJMPoints)
                {
                    jmPoints.Remove(p);// 移除
                }
            }

            // 更新已经存在的间瞄点属性，计算开始轰炸的间瞄点的位置（间瞄点从飞行变为轰炸时会计算是否发生了散布）
            List<JMPointsMsg> deletePoints = new List<JMPointsMsg>();// 轰炸300秒之后移除
            if (jmPoints.Count > 0)// 如果有间瞄点
            {
                for (int i = 0; i < jmPoints.Count; i++)
                {
                    JMPointsMsg jmp = jmPoints[i];// 当前更新的间瞄点
                    // 如果时正在飞行中的间瞄点
                    if (jmp.status == (int)JMPointStatusType.Flying)
                    {
                        jmp.fly_time++;// 飞行时间加一
                        if (jmp.fly_time >= 150)// 如果飞行了150秒，裁决落地点位
                        {
                            jmp.fly_time = 0;// 飞行时间置零，结束飞行

                            // 检查校射类型，调取校射类型对应的间瞄火力散布裁决表，根据结果修改间瞄点的位置

                            Random rr = new Random();
                            int r11 = rr.Next(1, 7);
                            int r12 = rr.Next(1, 7);
                            int random = r11 + r12;// 2-12的随机数，用于查间瞄火力散布裁决表
                            if (random < 2) random = 2;
                            if (random > 12) random = 12;

                            int index = -1;// 炮兵算子的索引
                            for (int j = 0; j < oprs.Count; j++)
                            {
                                if (oprs[j].obj_id == jmp.obj_id)// 如果是发射此间瞄点的炮兵算子
                                {                                 
                                    index = j;// 保存索引
                                }
                            }
                            if (index != -1)// 找到了发射间瞄火力的算子
                            {
                                OperatorMsg jm_opr = oprs[index];// 赋值
                                bool align_flag = false;// 是否有校射的标志位
                                bool target_align_flag = false;// 目标 校射 的标志位

                                // 检查是否存在目标
                                List<int> targetOprIDs = new List<int>();// 用于保存间瞄点格内的算子
                                for (int j = 0; j < oprs.Count; j++)
                                {
                                    if (oprs[j].cur_hex == jmp.pos && oprs[j].color != jm_opr.color)// 如果在间瞄点的位置，且不与进行间瞄的炮兵同阵营（不对同阵营的算子校射，逻辑上即不会有意攻击同阵营算子）
                                    {
                                        targetOprIDs.Add(oprs[j].obj_id);// 添加到targetOprIDs
                                    }
                                }
                                // 格内存在算子，检查是否可以目标校射
                                if (targetOprIDs.Count > 0)
                                {
                                    for (int j = 0; j < oprs.Count; j++)
                                    {
                                        // 排除不能进行校射的算子
                                        if (oprs[j].color != jm_opr.color)// 敌方算子不可帮助校射
                                        {
                                            continue;
                                        }
                                        if (oprs[j].type != (int)OprType.Vehicle && oprs[j].type != (int)OprType.Infantry && oprs[j].sub_type != (int)OprSubType.Drone)// 我方除了地面单位和无人机，其他算子都不能帮助校射
                                        {
                                            continue;
                                        }
                                        foreach (int id in targetOprIDs)
                                        {
                                            if (oprs[j].see_enemy_bop_ids.Contains(id))// 如果 能帮助校射的算子 能看见 间瞄点中的敌方算子（通视）
                                            {
                                                // 可以目标校射
                                                target_align_flag = true;// 进行了目标校射
                                                align_flag = true;// 进行了校射
                                                break;// 已经判断完毕，退出对targetOprIDs的遍历
                                            }
                                        }
                                        if (target_align_flag)// 放foreach循环外，以跳出for循环
                                        {
                                            // 目标校射，不论结果如何都不会改变pos，所以不调用裁决表
                                            break;// 如果进行了目标校射，就不需要继续寻找可以校射的算子，所以退出对oprs的遍历
                                        }
                                    }
                                }

                                // 如果格内无算子，或者算子不可视
                                if (!target_align_flag || targetOprIDs.Count == 0)
                                {
                                    // 检测是否可以格内校射
                                    for (int j = 0; j < oprs.Count; j++)
                                    {
                                        if (oprs[j].color != jm_opr.color) continue;// 敌方算子不可帮助校射
                                        if (oprs[j].type != (int)OprType.Vehicle && oprs[j].type != (int)OprType.Infantry && oprs[j].sub_type != (int)OprSubType.Drone)// 我方除了地面单位和无人机，其他算子都不能帮助校射
                                        {
                                            continue;
                                        }
                                        if (map.CanSee(oprs[j].cur_hex, jmp.pos))// 如果 可以提供校射的算子 可以看到间瞄计划格（通视）
                                        {
                                            // 可以格内校射
                                            #region 格内校射，查表修改pos
                                            int judgeTabValue = indirectedshootJudgeTab.GetHexAlignJMFireDispersal(map.GetDistance(jm_opr.cur_hex, jmp.pos), random);// 用炮兵和间瞄计划格之间的距离 和 随机数 查格内校射间瞄火力散布裁决表
                                            if (judgeTabValue == 1)// 如果散布一格
                                            {
                                                int[] allHex = map.GetNeighbors(jmp.pos);// 取得间瞄计划格的邻格
                                                Random r = new Random();
                                                int ind = r.Next(allHex.Length);// 生成随机数索引，随机取一个格子
                                                while (allHex[ind] == -1)// 如果取得的六角格超出地图边界
                                                {
                                                    ind = r.Next(allHex.Length);// 重新获取索引
                                                }
                                                jmp.pos = allHex[ind];// 更新间瞄点的位置
                                            }
                                            else if (judgeTabValue == 2)// 如果散布两格
                                            {
                                                List<int> allHex = new List<int>();// 保存一定范围内的六角格
                                                int row = jmp.pos / 100;// 取得间瞄计划格的横坐标
                                                int col = jmp.pos % 100;// 取得间瞄计划格的纵坐标
                                                // 获取pos为中心的5x5范围内的六角格
                                                for (int x = 0; x < 5; x++)// 取5行
                                                {
                                                    for (int y = 0; y < 5; y++)// 每行取5个六角格
                                                    {
                                                        int ro = row - 2 + x;// 以5x5正方形的坐上角为初始，横纵坐标分别加上x、y，得到对应位置的六角格
                                                        int co = col - 2 + y;
                                                        ro = ro < 0 ? 0 : ro;// 如果小于0就令其为0
                                                        co = co < 0 ? 0 : co;
                                                        ro = ro > map.MaxRow - 1 ? map.MaxRow - 1 : ro;// 如果大于地图边界就令其在地图边界
                                                        co = co > map.MaxCol - 1 ? map.MaxCol - 1 : co;
                                                        allHex.Add(ro * 100 + co);// 添加
                                                    }
                                                }
                                                allHex = allHex.Distinct().ToList();//去除重复项
                                                Random r = new Random();
                                                int ind = r.Next(allHex.Count);// 取得索引
                                                while (map.GetDistance(allHex[ind], jmp.pos) != 2)// 如果取得的六角格 与 间瞄计划点当前的位置 的距离 不为2
                                                {
                                                    ind = r.Next(allHex.Count);// 重新取得索引
                                                }
                                                jmp.pos = allHex[ind];// 更新间瞄点的位置
                                            }
                                            else if (judgeTabValue == 3)// 如果散布三格
                                            {
                                                List<int> allHex = new List<int>();
                                                int row = jmp.pos / 100;
                                                int col = jmp.pos % 100;
                                                // 获取pos为中心的7x7范围内的六角格
                                                for (int x = 0; x < 7; x++)
                                                {
                                                    for (int y = 0; y < 7; y++)
                                                    {
                                                        int ro = row - 3 + x;
                                                        int co = col - 3 + y;
                                                        ro = ro < 0 ? 0 : ro;
                                                        co = co < 0 ? 0 : co;
                                                        ro = ro > map.MaxRow - 1 ? map.MaxRow - 1 : ro;
                                                        co = co > map.MaxCol - 1 ? map.MaxCol - 1 : co;
                                                        allHex.Add(ro * 100 + co);
                                                    }
                                                }
                                                allHex = allHex.Distinct().ToList();//去除重复项
                                                Random r = new Random();
                                                int ind = r.Next(allHex.Count);
                                                while (map.GetDistance(allHex[ind], jmp.pos) != 3)
                                                {
                                                    ind = r.Next(allHex.Count);
                                                }
                                                jmp.pos = allHex[ind];
                                            }
                                            #endregion
                                            align_flag = true;// 完成了校射

                                            break;
                                        }
                                    }
                                }
                                // 没有校射，即无校射
                                if (!align_flag)
                                {
                                    #region 无校射，查表修改pos
                                    int judgeTabValue = indirectedshootJudgeTab.GetNoAlignJMFireDispersal(map.GetDistance(jm_opr.cur_hex, jmp.pos), random);// 用炮兵和间瞄计划格之间的距离 和 随机数 查格内校射间瞄火力散布裁决表
                                    if (judgeTabValue == 1)// 如果散布一格
                                    {
                                        int[] allHex = map.GetNeighbors(jmp.pos);// 取得间瞄计划格的邻格
                                        Random r = new Random();
                                        int ind = r.Next(allHex.Length);// 生成随机数索引，随机取一个格子
                                        while (allHex[ind] == -1)// 如果取得的六角格超出地图边界
                                        {
                                            ind = r.Next(allHex.Length);// 重新获取索引
                                        }
                                        jmp.pos = allHex[ind];// 更新间瞄点的位置
                                    }
                                    else if (judgeTabValue == 2)// 如果散布两格
                                    {
                                        List<int> allHex = new List<int>();
                                        int row = jmp.pos / 100;
                                        int col = jmp.pos % 100;
                                        // 获取pos为中心的5x5范围内的六角格
                                        for (int x = 0; x < 5; x++)// 取5行
                                        {
                                            for (int y = 0; y < 5; y++)// 每行取5个六角格
                                            {
                                                int ro = row - 2 + x;// 以5x5正方形的坐上角为初始，横纵坐标分别加上x、y，得到对应位置的六角格
                                                int co = col - 2 + y;
                                                ro = ro < 0 ? 0 : ro;// 如果小于0就令其为0
                                                co = co < 0 ? 0 : co;
                                                ro = ro > map.MaxRow - 1 ? map.MaxRow - 1 : ro;// 如果大于地图边界就令其在地图边界
                                                co = co > map.MaxCol - 1 ? map.MaxCol - 1 : co;
                                                allHex.Add(ro * 100 + co);// 添加
                                            }
                                        }
                                        allHex = allHex.Distinct().ToList();//去除重复项
                                        Random r = new Random();
                                        int ind = r.Next(allHex.Count);// 取得索引
                                        while (map.GetDistance(allHex[ind], jmp.pos) != 2)// 如果取得的六角格 与 间瞄计划点当前的位置 的距离 不为2
                                        {
                                            ind = r.Next(allHex.Count);// 重新取得索引
                                        }
                                        jmp.pos = allHex[ind];// 更新间瞄点的位置
                                    }
                                    else if (judgeTabValue == 3)// 如果散布三格
                                    {
                                        List<int> allHex = new List<int>();
                                        int row = jmp.pos / 100;
                                        int col = jmp.pos % 100;
                                        // 获取pos为中心的7x7范围内的六角格
                                        for (int x = 0; x < 7; x++)
                                        {
                                            for (int y = 0; y < 7; y++)
                                            {
                                                int ro = row - 3 + x;
                                                int co = col - 3 + y;
                                                ro = ro < 0 ? 0 : ro;
                                                co = co < 0 ? 0 : co;
                                                ro = ro > map.MaxRow - 1 ? map.MaxRow - 1 : ro;
                                                co = co > map.MaxCol - 1 ? map.MaxCol - 1 : co;
                                                allHex.Add(ro * 100 + co);
                                            }
                                        }
                                        allHex = allHex.Distinct().ToList();//去除重复项
                                        Random r = new Random();
                                        int ind = r.Next(allHex.Count);
                                        while (map.GetDistance(allHex[ind], jmp.pos) != 3)
                                        {
                                            ind = r.Next(allHex.Count);
                                        }
                                        jmp.pos = allHex[ind];
                                    }
                                    else if (judgeTabValue == 4)// 如果散布四格
                                    {
                                        List<int> allHex = new List<int>();
                                        int row = jmp.pos / 100;
                                        int col = jmp.pos % 100;
                                        // 获取pos为中心的9x9范围内的六角格
                                        for (int x = 0; x < 9; x++)
                                        {
                                            for (int y = 0; y < 9; y++)
                                            {
                                                int ro = row - 4 + x;
                                                int co = col - 4 + y;
                                                ro = ro < 0 ? 0 : ro;
                                                co = co < 0 ? 0 : co;
                                                ro = ro > map.MaxRow - 1 ? map.MaxRow - 1 : ro;
                                                co = co > map.MaxCol - 1 ? map.MaxCol - 1 : co;
                                                allHex.Add(ro * 100 + co);
                                            }
                                        }
                                        allHex = allHex.Distinct().ToList();//去除重复项
                                        Random r = new Random();
                                        int ind = r.Next(allHex.Count);
                                        while (map.GetDistance(allHex[ind], jmp.pos) != 4)
                                        {
                                            ind = r.Next(allHex.Count);
                                        }
                                        jmp.pos = allHex[ind];
                                    }
                                    #endregion
                                }
                            }
                            else
                            {
                                //炮兵被打掉
                            }
                            // 间瞄点校射裁决完成，更新后位置之后更新状态
                            jmp.status = (int)JMPointStatusType.Exploding;// 更改间瞄点状态为正在轰炸
                            jmp.boom_time = 0;// 轰炸时间开始计时
                        }
                    }
                    else if (jmp.status == (int)JMPointStatusType.Exploding)// 如果是正在轰炸的间瞄点
                    {
                        jmp.boom_time++;// 轰炸时间加一
                    }
                    // 删除超过时间的点
                    if (jmp.boom_time >= 300) deletePoints.Add(jmp);// 轰炸时间超过300秒，删除（tip：也可以超过300秒跳过添加的代码）
                    // 结构体是值类型，字段用的不是同一个地址
                    jmPoints[i] = jmp;
                }
            }
            if (deletePoints.Count > 0)// 如果有需要删除的间瞄点
            {
                foreach (var p in deletePoints)// 删除
                {
                    jmPoints.Remove(p);
                }
            }

            // 新增间瞄点
            if (newJMPoints.Count > 0)
            {
                foreach (var p in newJMPoints)
                {
                    jmPoints.Add(p);// 增加
                }
            }

            return jmPoints;
        }
        /// <summary>
        /// 从judgeResults中选出与shoot有关的，形成judge_info
        /// </summary>
        private List<JudgeInfoMsg> UpdateJudgeInfo(List<IJudgeResult> judgeResults, TimeMsg time)
        {
            // 射击裁决cur_step
            List<JudgeInfoMsg> judge_infos = new List<JudgeInfoMsg>();// 保存裁决信息
            foreach (IJudgeResult judge_result in judgeResults)
            {
                if (judge_result.ActionType == ActionType.Shoot && judge_result.IsSuccess)// 找到动作类型是直瞄射击，且成功（间瞄射击没有动作，动作类型暂设置为Shoot）
                {
                    //if ((bool)judge_result.GetType().GetProperty("IsShooting").GetValue(judge_result))
                    //{
                    JudgeType judgeType = (JudgeType)judge_result.GetType().GetProperty("Type").GetValue(judge_result);// 获取裁决类型
                    if (judgeType == JudgeType.Shoot)// 如果裁决类型是直瞄射击裁决
                    {
                        ShootingJudgeResult shootingJudgeResult = (ShootingJudgeResult)judge_result;// 强制转为直瞄射击裁决结果

                        JudgeInfoMsg judgeInfoMsg = new JudgeInfoMsg
                        {
                            att_level = shootingJudgeResult.AttLevel,// 攻击等级
                            att_obj_blood = shootingJudgeResult.AttObjBlood,// 攻击方的血量
                            att_obj_id = shootingJudgeResult.ObjID,// 攻击方算子的id
                            attack_color = (int)shootingJudgeResult.AttackColor,// 攻击方算子的颜色
                            attack_sub_type = (int)shootingJudgeResult.AttackSubType,// 攻击方算子的细分类型
                            cur_step = time.cur_step,// 当前步长
                            damage = shootingJudgeResult.Damage,// 最终战损
                            distance = shootingJudgeResult.Distance,// 攻击的距离
                            ele_diff = shootingJudgeResult.EleDiff,// 高差（被攻击方所在的高度 减去 攻击方所在的高度）
                            ori_damage = shootingJudgeResult.OriDamage,// 初始战损
                            random1 = shootingJudgeResult.Random1,// 随机数1
                            random2 = shootingJudgeResult.Random2,// 随机数2
                            random2_rect = shootingJudgeResult.Random2Rect,// 随机数2修正（修正后的）
                            rect_damage = shootingJudgeResult.RectDamage,// 战损修正值（用来修正的，修正完得到最终战损）
                            target_color = (int)shootingJudgeResult.TargetColor,// 攻击目标的颜色
                            target_obj_id = shootingJudgeResult.TargetObjID,// 攻击目标的id
                            target_sub_type = (int)shootingJudgeResult.TargetSubType,// 攻击目标的细分类型
                            type = (int)shootingJudgeResult.Type,// 裁决类型(直瞄、间瞄、引导射击)
                            wp_id = (int)shootingJudgeResult.WeaponID,// 攻击使用的武器id
                        };
                        judge_infos.Add(judgeInfoMsg);
                    }
                    if (judgeType == JudgeType.IndirectedShoot)
                    {
                        IndirectedShootJudgeResult indirectedShootJudgeResult = (IndirectedShootJudgeResult)judge_result;

                        JudgeInfoMsg judgeInfoMsg = new JudgeInfoMsg
                        {
                            align_status = (int)indirectedShootJudgeResult.AlignStatus,// 校射类型 0-无较射 1-格内较射 2-目标较射
                            att_obj_blood = indirectedShootJudgeResult.AttObjBlood,// 攻击方的血量
                            att_obj_id = indirectedShootJudgeResult.ObjID,// 攻击方算子的id
                            attack_color = (int)indirectedShootJudgeResult.AttackColor,// 攻击方算子的颜色
                            attack_sub_type = (int)indirectedShootJudgeResult.AttackSubType,// 攻击方算子的细分类型
                            cur_step = time.cur_step,// 当前步长
                            damage = indirectedShootJudgeResult.Damage,// 最终战损
                            distance = indirectedShootJudgeResult.Distance,// 攻击的距离
                            ori_damage = indirectedShootJudgeResult.OriDamage,// 初始战损
                            random1 = indirectedShootJudgeResult.Random1,// 随机数1
                            random2 = indirectedShootJudgeResult.Random2,// 随机数2
                            ori_random2 = indirectedShootJudgeResult.OriRandom2,// 原随机数2（实际上是第三个随机数）
                            random2_rect = indirectedShootJudgeResult.Random2Rect,// 第三个随机数的修正值（修正后的）
                            rect_damage = indirectedShootJudgeResult.RectDamage,// 战损修正值（用来修正的，修正完得到最终战损）
                            target_color = (int)indirectedShootJudgeResult.TargetColor,// 攻击目标的颜色
                            target_obj_id = indirectedShootJudgeResult.TargetObjID,// 攻击目标的id
                            target_sub_type = (int)indirectedShootJudgeResult.TargetSubType,// 攻击目标的细分类型
                            type = (int)indirectedShootJudgeResult.Type,// 裁决类型
                            wp_id = (int)indirectedShootJudgeResult.WeaponID,// 攻击使用的武器id
                            offset = indirectedShootJudgeResult.Offset// 是否散布(偏移)
                        };
                        judge_infos.Add(judgeInfoMsg);
                    }
                }
                else if (judge_result.ActionType == ActionType.GuideShoot && judge_result.IsSuccess)// 为引导射击动作，且成功执行
                {
                    GuideShootJudgeResult guideShootJudgeResult = (GuideShootJudgeResult)judge_result;// 强转为引导射击裁决结果

                    JudgeInfoMsg judgeInfoMsg = new JudgeInfoMsg
                    {
                        guide_opr_id = guideShootJudgeResult.ObjID,// 执行引导的算子
                        att_level = guideShootJudgeResult.AttLevel,// 攻击等级
                        att_obj_blood = guideShootJudgeResult.AttObjBlood,// 射击算子的血量
                        att_obj_id = guideShootJudgeResult.AttObjID,// 射击算子id
                        attack_color = (int)guideShootJudgeResult.AttackColor,// 射击方颜色
                        attack_sub_type = (int)guideShootJudgeResult.AttackSubType,// 射击算子的细分类型
                        cur_step = time.cur_step,// 当前步长
                        damage = guideShootJudgeResult.Damage,// 最终战损
                        distance = guideShootJudgeResult.Distance,// 射击距离
                        ele_diff = guideShootJudgeResult.EleDiff,// 高差（被攻击方所在的高度 减去 攻击方所在的高度）
                        ori_damage = guideShootJudgeResult.OriDamage,// 初始战损
                        random1 = guideShootJudgeResult.Random1,// 随机数1
                        random2 = guideShootJudgeResult.Random2,// 随机数2
                        random2_rect = guideShootJudgeResult.Random2Rect,// 随机数2修正（修正后的）
                        rect_damage = guideShootJudgeResult.RectDamage,// 战损修正值（用来修正的，修正完得到最终战损）
                        target_color = (int)guideShootJudgeResult.TargetColor,// 攻击目标的颜色
                        target_obj_id = guideShootJudgeResult.TargetObjID,// 攻击目标的id
                        target_sub_type = (int)guideShootJudgeResult.TargetSubType,// 攻击目标的细分类型
                        type = (int)guideShootJudgeResult.Type,// 裁决类型
                        wp_id = (int)guideShootJudgeResult.WeaponID,// 攻击使用的武器id
                    };
                    judge_infos.Add(judgeInfoMsg);
                }
            }
            return judge_infos;
        }

        private TimeMsg UpdateTime(TimeMsg lastTime, ref int curStep)
        {
            curStep++;// cur_step加一
            TimeMsg timeMsg = new TimeMsg 
            {
                cur_step = curStep,
                tick = lastTime.tick,
                max_time = lastTime.max_time
            };
            if (timeMsg.cur_step >= timeMsg.max_time) done = true;// 如果当前步长大于等于最大步长就结束推演，结束标志位设置为true
            else done = false;
            return timeMsg;
        }

        /// <summary>
        /// 根据裁决结果更新分数
        /// </summary>
        private ScoreMsg UpdateScores(List<OperatorMsg> operators, List<OperatorMsg> passengers, CityMsg[] cities)
        {
            // 初始化scores
            ScoreMsg cur_scores = new ScoreMsg();// 保存更新完的分数信息，用于返回
            int blue_remain_max = this.blue_remain_max;// 取得蓝方最大可获得的分数
            int red_remain_max = this.red_remain_max;// 取得红方最大可获得的分数

            cur_scores.blue_attack = 0;// 蓝方攻击得分
            cur_scores.blue_occupy = 0;// 蓝方夺控分
            cur_scores.blue_remain = 0;// 蓝方剩余得分
            cur_scores.blue_remain_max = blue_remain_max;// 蓝方最大剩余得分
            cur_scores.blue_total = 0;// 蓝方总分
            cur_scores.blue_win = 0;// 蓝方净胜分
            cur_scores.red_attack = 0;// 红方战斗得分
            cur_scores.red_occupy = 0;// 红方夺控分
            cur_scores.red_remain = 0;// 红方剩余算子分
            cur_scores.red_remain_max = red_remain_max;// 红方最大剩余得分
            cur_scores.red_total = 0;// 红方总分
            cur_scores.red_win = 0;// 红方净胜分

            // ===计算当前所有的得分===
            // 算子分数
            foreach (OperatorMsg opr in operators)// 遍历态势中剩下的算子
            {
                if (opr.color == (int)Color.Red)// 红方
                {
                    cur_scores.red_remain += (opr.value * opr.blood);// 红方剩余算子的分值 乘 算子剩余血量
                }
                else if (opr.color == (int)Color.Blue)// 蓝方
                {
                    cur_scores.blue_remain += (opr.value * opr.blood);// 蓝方剩余算子的分值 乘 算子剩余血量
                }               
            }
            foreach (OperatorMsg opr in passengers)// 遍历态势中剩下的乘客
            {
                if (opr.color == (int)Color.Red)
                {
                    cur_scores.red_remain += (opr.value * opr.blood);// 红方剩余乘客的分值 乘 剩余血量
                }
                else if (opr.color == (int)Color.Blue)
                {
                    cur_scores.blue_remain += (opr.value * opr.blood);// 蓝方剩余乘客的分值 乘 剩余血量
                }
            }
            // 夺控点分数
            foreach (CityMsg city in cities)
            {
                if (city.flag == (int)Color.Red)
                {
                    cur_scores.red_occupy += city.value;// 每个红方的夺控点的夺控点分数的和
                }
                else if (city.flag == (int)Color.Blue)
                {
                    cur_scores.blue_occupy += city.value;// 每个蓝方的夺控点的夺控点分数的和
                }
            }
            cur_scores.blue_attack = cur_scores.red_remain_max - cur_scores.red_remain;// 蓝方攻击得分等于 红方最大剩余得分 减 红方剩余算子分
            cur_scores.red_attack = cur_scores.blue_remain_max - cur_scores.blue_remain;// 红方攻击得分等于 蓝方最大剩余得分 减 蓝方剩余算子分
            cur_scores.blue_total = cur_scores.blue_attack + cur_scores.blue_occupy;// 总得分等于攻击得分加夺控得分
            cur_scores.red_total = cur_scores.red_attack + cur_scores.red_occupy;
            cur_scores.blue_win = cur_scores.blue_total - cur_scores.red_total;// 净胜分等于双方总分的差
            cur_scores.red_win = -cur_scores.blue_win;

            return cur_scores;
        }

        /// <summary>
        /// 根据夺控的裁决结果更新夺控点信息
        /// </summary>
        private CityMsg[] UpdateCities(List<IJudgeResult> judgeResults, CityMsg[] oldCities)
        {
            //夺控裁决结果更新
            // 复制，避免直接修改导致改变replayData中的数据
            CityMsg[] cities = new CityMsg[oldCities.Length];
            for (int i = 0; i < oldCities.Count(); i++)
            {
                CityMsg city = new CityMsg
                {
                    coord = oldCities[i].coord,
                    value = oldCities[i].value,
                    name = oldCities[i].name,
                    flag = oldCities[i].flag
                };
                cities[i] = city;// 添加进数组
            }
            foreach (var judge_result in judgeResults)// 遍历裁决结果
            {
                if (judge_result.ActionType == ActionType.Occupy && judge_result.IsSuccess)// 如果夺控裁决结果，且成功夺控
                {
                    OccupyJudgeResult occupyJudgeResult = (OccupyJudgeResult)judge_result;// 转为夺控裁决结果类型
                    for (int i = 0; i < cities.Count(); i++)
                    {
                        if (occupyJudgeResult.CityCoord == cities[i].coord)// 找到被夺控的夺控点
                        {
                            cities[i].flag = (int)occupyJudgeResult.OprColor;// 修改夺控点颜色为执行夺控的算子的颜色
                        }
                    }
                }
            }
            return cities;
        }

        private List<OperatorMsg> GetOprMsgs(List<IOperator> oprs)
        {
            // oprMsgs用来保存转换好的数据
            List<OperatorMsg> oprMsgs = new List<OperatorMsg>();

            for (int i = 0; i < oprs.Count; i++)
            {
                IOperator opr = oprs[i];
                OperatorMsg operatorMsg = new OperatorMsg();
                PropertyInfo[] properties = opr.GetType().GetProperties();  //算子类的全部属性
                FieldInfo[] msgFieldInfos = typeof(OperatorMsg).GetFields();   //算子数据结构的全部成员
                object boxedOperatorMsg = operatorMsg;      //装箱    （结构体是值类型，不是引用类型）
                foreach (var property in properties)
                {
                    foreach (var msgFieldInfo in msgFieldInfos)
                    {
                        // 不考虑大小写和下划线，如果名称一致
                        if (msgFieldInfo.Name.Replace("_", "").ToLower() == property.Name.ToLower())    // 字符串字母全部改成小写，去除下划线（属性大写，无下划线）
                        {
                            // 特殊数据结构（内含自定义枚举类型），无法用强转换来转换数据类型的，采取手动转换
                            if (property.Name == "CarryWeaponIDs")
                            {
                                List<int> carry_weapon_ids = new List<int>();
                                foreach (WeaponType id in (List<WeaponType>)property.GetValue(opr))
                                {
                                    carry_weapon_ids.Add((int)id);
                                }
                                msgFieldInfo.SetValue(boxedOperatorMsg, carry_weapon_ids.ToArray());
                                continue;
                            }
                            if (property.Name == "RemainBulletNums")
                            {
                                Dictionary<int, int> remain_bullet_nums = new Dictionary<int, int>();
                                foreach (var remain_bullet_num in (Dictionary<BulletType, int>)property.GetValue(opr))
                                {
                                    remain_bullet_nums.Add((int)remain_bullet_num.Key, remain_bullet_num.Value);
                                }
                                msgFieldInfo.SetValue(boxedOperatorMsg, remain_bullet_nums);
                                continue;
                            }
                            if (property.Name == "RemainBulletNumsBK")
                            {
                                Dictionary<int, int> remain_bullet_nums_bk = new Dictionary<int, int>();
                                foreach (var remain_bullet_num_bk in (Dictionary<BulletType, int>)property.GetValue(opr))
                                {
                                    remain_bullet_nums_bk.Add((int)remain_bullet_num_bk.Key, remain_bullet_num_bk.Value);
                                }
                                msgFieldInfo.SetValue(boxedOperatorMsg, remain_bullet_nums_bk);
                                continue;
                            }
                            if (property.Name == "ValidPassengerTypes")
                            {
                                List<int> valid_passenger_types = new List<int>();
                                foreach (var type in (List<OprSubType>)property.GetValue(opr))
                                {
                                    valid_passenger_types.Add((int)type);
                                }
                                msgFieldInfo.SetValue(boxedOperatorMsg, valid_passenger_types);
                                continue;
                            }
                            if (property.Name == "MaxPassengerNums")
                            {
                                Dictionary<int, int> max_passenger_nums = new Dictionary<int, int>();
                                foreach (var num in (Dictionary<OprSubType, int>)property.GetValue(opr))
                                {
                                    max_passenger_nums.Add((int)num.Key, num.Value);
                                }
                                msgFieldInfo.SetValue(boxedOperatorMsg, max_passenger_nums);
                                continue;
                            }
                            if (property.Name == "ObserveDistance")
                            {
                                Dictionary<int, int> observeDistance = new Dictionary<int, int>();
                                foreach (var num in (Dictionary<OprSubType, int>)property.GetValue(opr))
                                {
                                    observeDistance.Add((int)num.Key, num.Value);
                                }
                                msgFieldInfo.SetValue(boxedOperatorMsg, observeDistance);
                                continue;
                            }
                            // 深度拷贝后赋值给成员字段
                            object value = DeepCopy(property.GetValue(opr));
                            msgFieldInfo.SetValue(boxedOperatorMsg, value);
                        }//消息结构体的成员个数大于等于算子属性个数
                    }
                }
                OperatorMsg unboxedOperatorMsg = (OperatorMsg)boxedOperatorMsg; //拆箱
                oprMsgs.Add(unboxedOperatorMsg);
            }

            return oprMsgs;
        }
        private object DeepCopy(object source)
        {
            if (source == null)
                return null;

            // 创建一个内存流
            MemoryStream memoryStream = new MemoryStream();

            // 创建二进制格式化器
            BinaryFormatter formatter = new BinaryFormatter();

            // 将对象序列化到内存流中
            formatter.Serialize(memoryStream, source);

            // 将内存流指针位置归零
            memoryStream.Position = 0;

            // 从内存流中反序列化为新的对象副本
            object clone = formatter.Deserialize(memoryStream);

            // 关闭内存流
            memoryStream.Close();

            return clone;
        }

        private List<IBasicAction> GetActions(List<ActionMsg> actMsgs)
        {
            // 用来记录转换完的IBasicAction类型的动作
            List<IBasicAction> actions = new List<IBasicAction>();

            foreach (var actMsg in actMsgs)
            {
                // 如果可以根据int类型的actMsg.type得到枚举类型的actType（如果是记录过的动作类型）
                if (Enum.TryParse<ActionType>(actMsg.type.ToString(), out ActionType actType))
                {
                    // 创建具体的类对象，然后用父类接口IBasicAction封装
                    IBasicAction basicAction = (IBasicAction)Activator.CreateInstance(Type.GetType("DataType.Actions." + actType.ToString() + "Action, DataType"));
                    FieldInfo[] msgFieldInfos = typeof(ActionMsg).GetFields();  //取得ActionMsg的全部成员（字段）
                    PropertyInfo[] properties = basicAction.GetType().GetProperties();   //取得具体动作类的全部属性

                    object o = basicAction;     //  装箱      (这个虽然也是结构体，但是试了下这个好像可以不装箱)
                    foreach (var msgFieldInfo in msgFieldInfos)
                    {
                        foreach(var property in properties)
                        {
                            // 如果字段的名称与属性的名称一致（忽略大小写和下划线）
                            if(msgFieldInfo.Name.Replace("_", "").ToLower() == property.Name.ToLower())// msgFieldInfo.GetType()方法拿出来的类型是System.Reflection.RtFieldInfo
                            {
                                // 将字段的值深度拷贝，然后赋值给属性
                                object value = DeepCopy(msgFieldInfo.GetValue(actMsg));
                                property.SetValue(o, value);//拿消息结构体actMsg成员值，给basicAction属性赋值
                                break;
                            }//Msg结构成员数大于等于basicAction属性数
                        }
                    }
                    IBasicAction basicAct = (IBasicAction)o;
                    // 转换完成的动作存入actions中
                    actions.Add(basicAct);
                }else throw new Exception($"动作的类型ActionType有误！");// 是没有记录的动作类型
            }


            return actions;
        }

        private Dictionary<int, ObservationMsg> PartitionObservation(ObservationMsg observation, List<IOperator> deadOprs)
        {
            Dictionary<int, ObservationMsg> fullObservation = new Dictionary<int, ObservationMsg>();

            fullObservation.Add(-1, observation);
            fullObservation.Add((int)Color.Red, AssignObservation((int)Color.Red, observation, deadOprs));
            fullObservation.Add((int)Color.Blue, AssignObservation((int)Color.Blue, observation, deadOprs));
            return fullObservation;
        }
        private ObservationMsg AssignObservation(int color, ObservationMsg observation, List<IOperator> deadOprs)
        {
            List<ActionMsg> actions = new List<ActionMsg>();
            foreach (ActionMsg action in observation.actions)
            {
                int oprColor = GetOprColorFromID(action.obj_id, observation);
                if (oprColor == -1)
                {
                    // 算子被击毁删除
                    if (deadOprs.Count > 0)
                    {
                        foreach (var o in deadOprs)
                        {
                            if (o.ObjID == action.obj_id)
                            {
                                oprColor = (int)o.GetType().GetProperty("Color").GetValue(o);
                                break;
                            }
                        }
                    }  
                }
                if (oprColor == -1)
                {
                    Trace.TraceWarning($"GetOprColorFromID失败:态势和本次step被摧毁的算子中没有ID为{action.obj_id}的算子信息");
                }

                if (oprColor == color)
                {
                    actions.Add(action);
                }
            }

            // 更新valid_actions
            Dictionary<int, Dictionary<int, List<ActionMsg>>> valid_actions = new Dictionary<int, Dictionary<int, List<ActionMsg>>>();
            foreach (KeyValuePair<int, Dictionary<int, List<ActionMsg>>> opr_actions in observation.valid_actions)
            {
                int obj_id = opr_actions.Key;
                Dictionary<int, List<ActionMsg>> individual_valid_actions = opr_actions.Value;
                int oprColor = GetOprColorFromID(obj_id, observation);
                if (oprColor == -1)
                {
                    Trace.TraceWarning($"GetOprColorFromID失败:态势中没有ID为{obj_id}的算子信息");
                }
                if (oprColor == color)
                {
                    valid_actions.Add(obj_id, individual_valid_actions);
                }
            }

            // 更新operators
            List<OperatorMsg> operators = new List<OperatorMsg>();
            List<OperatorMsg> our_operators = new List<OperatorMsg>();
            List<OperatorMsg> enemies = new List<OperatorMsg>();
            foreach (OperatorMsg opr in observation.operators)
            {
                if (opr.color == color)
                {
                    operators.Add(opr);
                    our_operators.Add(opr);
                }
                else
                {
                    enemies.Add(opr);
                }
            }
            for (int i = 0; i < enemies.Count; i++)
            {
                OperatorMsg enemy = enemies[i];
                bool can_be_seen = false;
                foreach (OperatorMsg opr in our_operators)
                {
                    if (opr.see_enemy_bop_ids == null) continue;
                    if (opr.see_enemy_bop_ids.Contains(enemy.obj_id))
                    {
                        can_be_seen = true;
                        break;
                    }
                }
                if (can_be_seen)  // 能被看见的情况，隐藏迷雾属性
                {
                    enemy.remain_bullet_nums = new Dictionary<int, int> { { 0, -1 }, { 100, -1 }, { 101, -1 }, { 102, -1 } };
                    enemy.move_to_stop_remain_time = -1;
                    enemy.can_to_move = false;
                    //if (enemy.move_path == null) enemy.move_path = new List<int>();
                    //else enemy.move_path.Clear();
                    enemy.move_path = new List<int>();
                    enemy.tire_accumulate_time = -1;
                    enemy.keep_remain_time = -1;
                    enemy.launcher = -1;
                    //if (enemy.passenger_ids == null) enemy.passenger_ids = new List<int>();
                    //else enemy.passenger_ids.Clear();
                    enemy.passenger_ids = new List<int>();
                    //if (enemy.launch_ids == null) enemy.launch_ids = new List<int>();
                    //else enemy.launch_ids.Clear();
                    enemy.launch_ids = new List<int>();
                    enemy.alive_remain_time = -1;
                    enemy.get_on_remain_time = -1;
                    enemy.get_off_remain_time = -1;
                    enemy.weapon_unfold_time = -1;
                    //if (enemy.see_enemy_bop_ids == null) enemy.see_enemy_bop_ids = new List<int>();
                    //else enemy.see_enemy_bop_ids.Clear();
                    enemy.see_enemy_bop_ids = new List<int>();
                    enemy.C2 = -1;
                    enemy.C3 = -1;
                    enemy.target_state = -1;
                    operators.Add(enemy);
                }
            }

            // 更新passengers
            List<OperatorMsg> passengers = new List<OperatorMsg>();
            List<OperatorMsg> our_passengers = new List<OperatorMsg>();
            List<OperatorMsg> enemy_passengers = new List<OperatorMsg>();
            foreach (OperatorMsg opr in observation.passengers)
            {
                if (opr.color == color)
                {
                    passengers.Add(opr);
                    our_passengers.Add(opr);
                }
                else
                {
                    enemy_passengers.Add(opr);
                }
            }
            for (int i = 0; i < enemy_passengers.Count; i++)
            {
                OperatorMsg enemy = enemy_passengers[i];
                bool can_be_seen = false;
                foreach (OperatorMsg opr in our_passengers)
                {
                    if (opr.see_enemy_bop_ids.Contains(enemy.obj_id))
                    {
                        can_be_seen = true;
                        break;
                    }
                }
                if (can_be_seen)  // 能被看见的情况，隐藏迷雾属性
                {
                    enemy.remain_bullet_nums = new Dictionary<int, int> { { 0, -1 }, { 100, -1 }, { 101, -1 }, { 102, -1 } };
                    enemy.move_to_stop_remain_time = -1;
                    enemy.can_to_move = false;
                    //if (enemy.move_path == null) enemy.move_path = new List<int>();
                    //else enemy.move_path.Clear();
                    enemy.move_path = new List<int>();
                    enemy.tire_accumulate_time = -1;
                    enemy.keep_remain_time = -1;
                    enemy.launcher = -1;
                    //if (enemy.passenger_ids == null) enemy.passenger_ids = new List<int>();
                    //else enemy.passenger_ids.Clear();
                    enemy.passenger_ids = new List<int>();
                    //if (enemy.launch_ids == null) enemy.launch_ids = new List<int>();
                    //else enemy.launch_ids.Clear();
                    enemy.launch_ids = new List<int>();
                    enemy.alive_remain_time = -1;
                    enemy.get_on_remain_time = -1;
                    enemy.get_off_remain_time = -1;
                    enemy.weapon_unfold_time = -1;
                    //if (enemy.see_enemy_bop_ids == null) enemy.see_enemy_bop_ids = new List<int>();
                    //else enemy.see_enemy_bop_ids.Clear();
                    enemy.see_enemy_bop_ids = new List<int>();
                    enemy.C2 = -1;
                    enemy.C3 = -1;
                    enemy.target_state = -1;
                    passengers.Add(enemy);
                }
            }

            // 更新CurObservation[color]
            ObservationMsg observationMsg = new ObservationMsg
            {
                cities = observation.cities,
                scores = observation.scores,
                time = observation.time,
                judge_info = observation.judge_info,
                actions = actions,
                valid_actions = valid_actions,
                operators = operators,
                passengers = passengers,
                jm_points = observation.jm_points
            };
            return observationMsg;
        }
        private int GetOprColorFromID(int obj_id, ObservationMsg observation)
        {
            foreach (OperatorMsg opr in observation.operators)
            {
                if (opr.obj_id == obj_id) return opr.color;
            }
            return -1;
            //Trace.TraceWarning($"GetOprFromID失败:态势中没有ID为{obj_id}的算子");
            //return new OperatorMsg();
        }
        private List<ActionMsg> UpdateActions(List<ActionMsg> actions)
        {
            // 保存最终会留下的动作指令
            List<ActionMsg> actionMsgs = new List<ActionMsg>();
            // 记录对单个算子的重复或多个动作
            List<ActionMsg> remove_actions = new List<ActionMsg>();
            // 记录已经检查过的算子id
            List<int> oprs_id = new List<int>();
            // 当前动作向后检索，记录后面的对同一算子的动作
            try
            {
                for (int i = 0; i < actions.Count; i++)
                {
                    // 如果是检查过的算子就跳过，如果不是就添加进oprs_id并向后查找
                    if (oprs_id.Contains(actions[i].obj_id)) continue;
                    oprs_id.Add(actions[i].obj_id);
                    for (int j = i + 1; j < actions.Count; j++)
                    {
                        // 找到对同一个算子的另一个命令
                        if (actions[i].obj_id == actions[j].obj_id)
                        {
                            // 添加进remove_actions
                            remove_actions.Add(actions[j]);
                            Trace.TraceWarning($"无效指令：对{actions[i].obj_id}算子下达了多条动作指令");
                        }
                    }
                }
            }
            catch(IndexOutOfRangeException)
            {
                Trace.TraceWarning($"UpdateActions：遍历时索引超出了actions集合");// 迷之异常，没有解决
            }
            // 删除remove_actions中记录的动作
            foreach (ActionMsg remove_action in remove_actions) { actions.Remove(remove_action); }

            // 特殊检查
            remove_actions.Clear();// 清空，准备继续记录
            try
            {
                foreach (var a in actions)
                {
                    // 特殊情况一：某算子同时被引导射击和直瞄射击-->保留直瞄射击
                    // 如果是引导射击
                    if (a.type == (int)ActionType.GuideShoot)
                    {
                        foreach (var a_1 in actions)
                        {
                            // 如果是直瞄射击，并且射击算子与被引导算子一致
                            if (a_1.type == (int)ActionType.Shoot && a_1.obj_id == a.guided_obj_id)
                            {
                                // 引导射击动作添加至remove_actions，准备删除
                                remove_actions.Add(a);
                                Trace.TraceWarning($"无效指令：对{a_1.obj_id}算子下达直瞄射击命令的同时，{a_1.obj_id}算子也是{a.obj_id}算子执行引导射击的引导目标，引导射击命令下达失败");
                            }
                        }
                    }
                    // 特殊情况二：同时被下达其他命令和作为上车目标-->不执行上车
                    // 如果为上车命令
                    else if (a.type == (int)ActionType.GetOn)
                    {
                        foreach (var a_1 in actions)
                        {
                            // 如果上车命令的目标算子还被下达了其他命令
                            if (a_1.obj_id == a.target_obj_id)
                            {
                                // 上车命令添加至remove_actions，准备删除
                                remove_actions.Add(a);
                                Trace.TraceWarning($"无效指令：对{a_1.obj_id}算子下达命令的同时，{a_1.obj_id}算子也是{a.obj_id}算子执行上车动作的目标，上车命令下达失败");
                            }
                        }
                    }
                    // 特殊情况三：同时被下达发送信息指令和 作为接收信息的目标-->不执行接收
                    // 如果为发送信息命令
                    else if (a.type == (int)ActionType.SendMsg)
                    {
                        foreach (var a_1 in actions)
                        {
                            // 如果是发送信息，并且接收信息的算子是本次执行发送信息的算子
                            if (a_1.type == (int)ActionType.SendMsg && a_1.receiver_id == a.obj_id)
                            {
                                // 发送信息动作添加至remove_actions，准备删除
                                remove_actions.Add(a);
                                Trace.TraceWarning($"无效指令：{a.obj_id}算子在执行发送信息指令的同时，是{a_1.obj_id}算子发送信息的接收方，后者不再执行");
                            }
                        }
                    }
                }
            }
            catch (IndexOutOfRangeException)
            {
                Trace.TraceWarning($"UpdateActions：遍历时索引超出了actions集合");// 迷之异常，没有解决
            }
            
            // 删除remove_actions中记录的动作
            foreach (ActionMsg remove_action in remove_actions) { actions.Remove(remove_action); }

            // valid_actions检查，即保存valid_actions中记录的动作
            foreach (ActionMsg action in actions)
            {
                // 如果上个态势的valid_actions中记录了action算子的合法动作
                if (observation.valid_actions.TryGetValue(action.obj_id, out Dictionary<int, List<ActionMsg>> opr_valid_actions))     
                {
                    // 如果该算子的合法动作中有与action类型一致的动作
                    if (opr_valid_actions.TryGetValue(action.type, out List<ActionMsg> list_a))
                    {
                        if (list_a.Count > 0)
                        {
                            // 如果是机动类型直接添加
                            if (action.type == (int)ActionType.Move)
                            {
                                actionMsgs.Add(action);
                            }
                            // 如果是直瞄射击
                            else if (action.type == (int)ActionType.Shoot)
                            {
                                foreach (ActionMsg a in list_a)
                                {
                                    // 射击目标和使用武器都与valid_actions中记录的一致，则添加
                                    if (action.target_obj_id == a.target_obj_id && action.weapon_id == a.weapon_id)
                                    {
                                        actionMsgs.Add(action);
                                        break;
                                    }
                                }
                            }
                            // 如果是夺控，添加
                            else if (action.type == (int)ActionType.Occupy)
                            {
                                actionMsgs.Add(action);
                            }
                            // 如果是停止机动，添加
                            else if (action.type == (int)ActionType.StopMove)
                            {
                                actionMsgs.Add(action);
                            }
                            // 如果是武器锁定，添加
                            else if (action.type == (int)ActionType.WeaponLock)
                            {
                                actionMsgs.Add(action);
                            }
                            // 如果是武器展开，添加
                            else if (action.type == (int)ActionType.WeaponUnfold)
                            {
                                actionMsgs.Add(action);
                            }
                            // 如果是切换状态
                            else if (action.type == (int)ActionType.ChangeState)
                            {
                                foreach (ActionMsg a in list_a)
                                {
                                    // 如果切换的目标状态合法，添加
                                    if (action.target_state == a.target_state)
                                    {
                                        actionMsgs.Add(action);
                                        break;
                                    }
                                }
                            }
                            // 如果是上车
                            else if (action.type == (int)ActionType.GetOn)
                            {
                                foreach (ActionMsg a in list_a)
                                {
                                    // 如果是可以上车的目标，添加
                                    if (action.target_obj_id == a.target_obj_id)
                                    {
                                        actionMsgs.Add(action);
                                        break;
                                    }
                                }
                            }
                            // 如果是下车
                            else if (action.type == (int)ActionType.GetOff)
                            {
                                foreach (ActionMsg a in list_a)
                                {
                                    // 如果是可以下车的目标，添加
                                    if (action.target_obj_id == a.target_obj_id)
                                    {
                                        actionMsgs.Add(action);
                                        break;
                                    }
                                }
                            }
                            // 如果是间瞄计划
                            else if (action.type == (int)ActionType.JMPlan)
                            {
                                foreach (ActionMsg a in list_a)
                                {
                                    // 如果是可以间瞄计划的武器，添加
                                    if (action.weapon_id == a.weapon_id)
                                    {
                                        actionMsgs.Add(action);
                                        break;
                                    }
                                }
                            }
                            // 如果是取消间瞄计划，添加
                            else if (action.type == (int)ActionType.CancelJMPlan)
                            {
                                actionMsgs.Add(action);
                            }
                            // 如果是引导射击
                            else if (action.type == (int)ActionType.GuideShoot)
                            {
                                foreach (ActionMsg a in list_a)
                                {
                                    // 如果是合法的引导射击（武器，射击目标，引导对象），添加
                                    if (action.weapon_id == a.weapon_id && action.target_obj_id == a.target_obj_id && action.guided_obj_id == a.guided_obj_id)
                                    {
                                        actionMsgs.Add(action);
                                        break;
                                    }
                                }
                            }
                            // 如果是发送信息
                            else if (action.type == (int)ActionType.SendMsg)
                            {
                                actionMsgs.Add(action);
                            }
                            else
                            {
                                Trace.TraceWarning($"传入的动作类型有错误: {action.type}");// 没有记录的动作类型
                            }
                        }
                    }
                }
                else
                {
                    Trace.TraceWarning($"提取valid_action失败：valid_action键值对中没有以算子{action.obj_id}为键的数据");// 是上个态势没有保存此算子的合法动作（是不存在的算子）
                }
            }

            return actionMsgs;
        }
    }
}
